package com.blog.cloud.core.aspect;

import com.blog.cloud.core.annotation.RedisLock;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class RedisLockAspect {

    @Resource
    private RedissonClient redisson;

    /**
     * 前置增强
     */
    @SneakyThrows
    @Around(value = "@annotation(redisLock)")
    public Object around(ProceedingJoinPoint pjp, RedisLock redisLock) {
        String lockName = redisLock.lockName();
        long waitTime = redisLock.waitTime();
        long expireTime = redisLock.expireTime();
        RedisLock.RedisLockType redisLockType = redisLock.redisLockType();

        if (redisLockType == RedisLock.RedisLockType.READ) {
            log.info("the redis lock type is read lock");
            RReadWriteLock readWriteLock = redisson.getReadWriteLock(lockName);
            RLock readLock = readWriteLock.readLock();
            boolean flag = readLock.tryLock(waitTime, expireTime, TimeUnit.SECONDS);
            // 执行切点方法
            Object proceed;
            try {
                proceed = pjp.proceed();
            } finally {
                if (flag) {
                    readLock.unlock();
                }
            }
            return proceed;
        } else if (redisLockType == RedisLock.RedisLockType.WRITE) {
            log.info("the redis lock type is write lock");
            RReadWriteLock readWriteLock = redisson.getReadWriteLock(lockName);
            RLock writeLock = readWriteLock.writeLock();
            boolean flag = writeLock.tryLock(waitTime, expireTime, TimeUnit.SECONDS);
            // 执行切点方法
            Object proceed;
            try {
                proceed = pjp.proceed();
            } finally {
                if (flag) {
                    writeLock.unlock();
                }
            }
            return proceed;
        } else {
            log.info("the redis lock type is traditional lock");
            RLock lock = redisson.getLock(lockName);
            boolean flag = lock.tryLock(waitTime, expireTime, TimeUnit.SECONDS);
            // 执行切点方法
            Object proceed;
            try {
                proceed = pjp.proceed();
            } finally {
                if (flag) {
                    lock.unlock();
                }
            }
            return proceed;
        }
    }

}
